Click on the example application names below to look at the source code:
#include// 02.04.22 - Serial: Echo Back Characters sent to MCU as all caps // and when Carriage Return ("Enter") sent, send full string // // PIC16F627's Built in USART used, connected to MAX232 // // // Hardware Notes: // PIC16F827 Running at 4 MHz with External Oscillator // RB1 - Serial Receive // RB2 - Serial Transmit // // Global Variables unsigned int RTC = 0; // Real Time Clock Counter char StringOut[27] = "\r\nHello\r\n\0 "; // 1 2 345678 9 0 1234567890123456 unsigned char CharOutIndex = 0; unsigned char CharInIndex = 3; unsigned char SingleCharFlag = 0; // Set to Send a Single Byte at "CharOutIndex" // Configuration Fuses #if defined(_16F627) #warning PIC16F627 with external XT oscillator selected __CONFIG(0x03F61); // PIC116F627 Configuration Fuses: // - External "XT" Oscillator // - RA6/RA7 Digital I/O // - External Reset // - 70 msecs Power Up Timer On // - Watchdog Timer Off // - Code Protection Off // - BODEN Enabled #else #error Unsupported PICmicro MCU selected #endif // Interrupt Handler void interrupt tmr0_int(void) // TMR0 Interrupt Handler { unsigned char temp; if (T0IF) { T0IF = 0; // Reset Interrupt Flag RTC++; // Increment the Clock // Put additional interface code for 1 msec interrupt here } // endif if (TXIF) { // Send out String Character TXIF = 0; // Reset Interrupt if ((temp = StringOut[CharOutIndex]) == '\0') if (SingleCharFlag) // Just Echoing Back? SingleCharFlag = 0; else // No, Start new String StringOut[2] = '\"'; // Initialize CAPS string else { TXREG = temp; // Output the Next Character CharOutIndex++; // and Point to Next } // endif } // endif if (RCIF) { // Serial Character Received if ((temp = RCREG) < 0x020) // Control Character received? if (temp == 0x008) // BackSpace? if (CharInIndex > 3) { // Yes, and Something to Backspace StringOut[CharOutIndex = CharInIndex] = temp; // Backspace, Put in Blank, Backspace StringOut[CharInIndex + 1] = ' '; StringOut[CharInIndex + 2] = temp; StringOut[CharInIndex + 3] = '\0'; CharInIndex--; TXIF = 1; SingleCharFlag = 1; } else; else if (temp == 0x00D) { // "Enter", End String and Print StringOut[CharInIndex++] = '\"'; StringOut[CharInIndex++] = '\r'; StringOut[CharInIndex++] = '\n'; StringOut[CharInIndex] = '\0'; CharInIndex = 3; CharOutIndex = 0; TXIF = 1; } else; else { // ASCII Char if ((temp >= 'a') && (temp <= 'z')) temp -= ('a' - 'A'); if (CharInIndex < 23) { // Echo Back StringOut[CharOutIndex = CharInIndex] = temp; StringOut[++CharInIndex] = '\0'; // Make into String TXIF = 1; SingleCharFlag = 1; } // endif } // endif RCIF = 0; // Reset Interrupt Request } // endif } // End Interrupt Handler // Mainline void main(void) // Template Mainline { unsigned char temp; // Temporary Storage Value OPTION = 0x0D1; // Assign Prescaler to TMR0 // Prescaler is /4 TMR0 = 0; // Reset the Timer for Start T0IE = 1; // Enable Timer Interrupts GIE = 1; // Enable Interrupts // Put in Interface initialization code here SPBRG = 51; // 1200 bps @ 4 MHz TXEN = 1; // Enable the USART CREN = 1; SPEN = 1; PEIE = 1; // Enable PIE Interrupt Sources temp = RCREG; RCIF = 0; // Enable Receive Interrupt RCIE = 1; TXIF = 1; // Enable Transmit Interrupt TXIE = 1; while (1 == 1) { // Loop forever // Put in Robot high level operation code here } // endwhile } // End of Mainline
While being a very simple application, \code\ledflash\ledflash.c demonstrates some of the basic concepts that are presented in this book. Included on the CD-ROM is the \code\ledflash\rtc.wat file used to monitor the changing value of the "RTC" variable.
The "LED Enable" function, which allows the LED to flash or stay off is:
void enableLED(int LEDstate) // Set "eLED" according to
{ // "LEDstate"
LED = LEDoff; // Start with LED Off
if (LEDstate)
trisLED = 0; // Make LED Bit Output
else
trisLED = 1; // Make LED Bit Output
} // End enableLED
The \code\ledflash\ledflash.c application is:
#include// Flash an LED 1x per second // // Use TMR0, 1 msec interrupt as base // // 02.04.09 - Modified for "LEDFlash" Application // 02.03.28 - Updated to allow PIC16F627/PIC16F84 PICmicro MCUs // 02.01.23 - Originally created by myke predko // // // Hardware Notes: // PIC16F84/PIC16F627 running at 4 MHz // PIC16F627 uses internal 4 MHz oscillator // External _MCLR connection required // "LED" - RB1/LED Control Output // // Configuration Fuses #if defined (_16F84) #warning PIC16F84 selected __CONFIG(0x03FF1); // PIC16F84 Configuration Fuses: // - XT Oscillator // - 70 msecs Power Up Timer On // - Watchdog Timer Off // - Code Protection Off #elif defined(_16F627) #warning PIC16F627 with internal oscillator selected __CONFIG(0x03F70); // PIC116F627 Configuration Fuses: // - Internal Oscillator // - RA6/RA7 Digital I/O // - External Reset // - 70 msecs Power Up Timer On // - Watchdog Timer Off // - Code Protection Off // - BODEN Enabled #else #error Unsupported PICmicro MCU selected #endif // Global Variables volatile unsigned int RTC = 0; // Real Time Clock Counter static bit trisLED @ (unsigned) &TRISB*8+1; // LED Physical Bits static bit LED @ (unsigned) &PORTB*8+1; const int LEDon = 0; // Declare values for LED ON/off const int LEDoff = 1; // Interrupt Handler void interrupt tmr0_int(void) // TMR0 Interrupt Handler { if (T0IF) { T0IF = 0; // Reset Interrupt Flag RTC++; // Increment the Clock // Put mechalogic/elelogic interface code for 1 msec interrupt here if ((RTC % 512) == 0) LED = LED ^ 1; // Toggle LED Bit every 512 msecs } // endif // Put interrupt handlers for other mechalogic/elelogic interface code } // End Interrupt Handler void enableLED(int LEDstate) // Set "eLED" according to { // "LEDstate" LED = LEDoff; // Start with LED Off if (LEDstate) trisLED = 0; // Make LED Bit Output else trisLED = 1; // Make LED Bit Output } // End enableLED // Mainline void main(void) // Template Mainline { TMR0 = 0; // Reset the Timer for Start OPTION = 0x0D1; // Assign Prescaler to TMR0 // Prescaler is /4 T0IE = 1; // Enable Timer Interrupts GIE = 1; // Enable Interrupts // Put hardware interface initialization code here enableLED(1); // Start the LED Flashing while (1 == 1) { // Loop forever // Put robot biologic code here } // endwhile } // End of Mainline
| Number of LEDs | Individual LED Control | Algorithmic LED Control | Select LED Control | Improved Algoithmic LED Control |
|---|---|---|---|---|
| 1 | ledpwm.c | |||
| 2 | ledpwm2.c | ledpwm2a.c | ledpwm2b.c | |
| 3 | ledpwm3.c | ledpwm3a.c | ledpwm3b.c | ledpwm3c.c |
The basic \code\ledpwm\ledpwm.c application code is:
#include// Output a PWM Signal on a single LED (on RB1) // // Use the 1 msec TMR0 interrupt to create a 32 Hz PWM signal // // 02.04.10 - Created Application // 02.03.28 - Updated to allow PIC16F627/PIC16F84 PICmicro MCUs // 02.01.23 - Originally created by myke predko // // // Hardware Notes: // PIC16F84/PIC16F627 running at 4 MHz // PIC16F627 uses internal 4 MHz oscillator // External _MCLR connection required // "LED" - RB1/LED Control Output // // Configuration Fuses #if defined (_16F84) #warning PIC16F84 selected __CONFIG(0x03FF1); // PIC16F84 Configuration Fuses: // - XT Oscillator // - 70 msecs Power Up Timer On // - Watchdog Timer Off // - Code Protection Off #elif defined(_16F627) #warning PIC16F627 with internal oscillator selected __CONFIG(0x03F70); // PIC116F627 Configuration Fuses: // - Internal Oscillator // - RA6/RA7 Digital I/O // - External Reset // - 70 msecs Power Up Timer On // - Watchdog Timer Off // - Code Protection Off // - BODEN Enabled #else #error Unsupported PICmicro MCU selected #endif // Global Variables volatile int RTC = 0; // Real Time Clock Counter char PWMCycle; // Cycles are from 0 to 29 char PWMDuty; // Start with full on volatile int PWMLoop; // PWM Loop Count static bit trisLED @ (unsigned) &TRISB*8+1; // LED Physical Bits static bit LED @ (unsigned) &PORTB*8+1; const int LEDon = 0; // Declare values for LED ON/off const int LEDoff = 1; // Interrupt Handler void interrupt tmr0_int(void) // TMR0 Interrupt Handler { if (T0IF) { T0IF = 0; // Reset Interrupt Flag RTC++; // Increment the Clock // Put additional interface code for 1 msec interrupt here switch(PWMDuty) { // Look for extremes first case 0: // All off LED = LEDoff; break; case 29: // Always On (100%) LED = LEDon; break; default: // Else, something in between // If the cycle count is less // than the current then LED on if (PWMCycle <= PWMDuty) LED = LEDon; else LED = LEDoff; } // endswitch if (++PWMCycle == 30) { // Roll around cycle count? PWMCycle = 0; // Yes PWMLoop++; // Increment the Loop Counter } // endif } // endif // Put Different Interrupt Handlers here } void enableLED(int LEDstate) // Set "eLED" according to { // "LEDstate" LED = LEDoff; // Start with LED Off if (LEDstate) { PWMCycle = 0; // Cycles are from 0 to 29 PWMDuty = 29; // Start with full on PWMLoop = 0; // PWM Loop Count starting at zero trisLED = 0; // Make LED Bit Output } else trisLED = 1; // Make LED Bit Input/Stop Display } // End enableLED // Mainline void main(void) // Template Mainline { OPTION = 0x0D1; // Assign Prescaler to TMR0 // Prescaler is /4 TMR0 = 0; // Reset Timer before starting application T0IE = 1; // Enable Timer Interrupts GIE = 1; // Enable Interrupts // Put in Interface initialization code here enableLED(1); // Enable the LED Bit while (1 == 1) { // Loop forever // Put in Robot high level operation code here if (PWMLoop == 2) { // Change PWM Duty Cycle if (PWMDuty == 0) PWMDuty = 29; else PWMDuty--; PWMLoop = 0; // Reset Counter } // endif } // endwhile } // End of Mainline
For debugging the application code, I created the \code\beeper\beeper.wat watch file.
#include// "Beeper" - Beep a speaker for 1 Second On, followed by // One Second off // // TMR0 1 msec interrupt used to produce ~500 Hz Signal // // 02.03.28 - Updated to allow PIC16F627/PIC16F84 PICmicro MCUs // 02.01.23 - Originally created by myke predko // // // Hardware Notes: // PIC16F84/PIC16F627 running at 4 MHz // PIC16F627 uses internal 4 MHz oscillator // External _MCLR connection required // RB4 - Connected to a 0.47 uF cap and piezo electric speaker // // Configuration Fuses #if defined (_16F84) #warning PIC16F84 selected __CONFIG(0x03FF1); // PIC16F84 Configuration Fuses: // - XT Oscillator // - 70 msecs Power Up Timer On // - Watchdog Timer Off // - Code Protection Off #elif defined(_16F627) #warning PIC16F627 with internal oscillator selected __CONFIG(0x03F70); // PIC116F627 Configuration Fuses: // - Internal Oscillator // - RA6/RA7 Digital I/O // - External Reset // - 70 msecs Power Up Timer On // - Watchdog Timer Off // - Code Protection Off // - BODEN Enabled #else #error Unsupported PICmicro MCU selected #endif // Global Variables volatile unsigned int RTC = 0; // Real Time Clock Counter unsigned char BeeperFlag = 0; // Enable Beeper Flag static bit trisBeeper @ (unsigned) &TRISB*8+4; // RB4 - Beeper Physical Bits static bit Beeper @ (unsigned) &PORTB*8+4; // Interrupt Handler void interrupt tmr0_int(void) // TMR0 Interrupt Handler { if (T0IF) { T0IF = 0; // Reset Interrupt Flag RTC++; // Increment the Clock // Put mechalogic/elelogic interface code for 1 msec interrupt here if (BeeperFlag) // Toggle the Beeper I/O Pin Beeper ^= 1; // If Flag != 0 } // endif // Put interrupt handlers for other mechalogic/elelogic interface code } // End Interrupt Handler void Dlay(unsigned int msecs) // Delay a specified number of msecs { unsigned int DlayEnd; DlayEnd = RTC + msecs + 1; // Setup Delay so that it waits a // MINIMUM number of "msecs" while (DlayEnd != RTC); } // End Dlay void enableBeeper(void) // Enable the Beeper Output Bits { trisBeeper = 0; // Set the Beeper I/O Pin to Output } // End enableBeeper // Mainline void main(void) // Template Mainline { TMR0 = 0; // Reset the Timer for Start OPTION = 0x0D1; // Assign Prescaler to TMR0 // Prescaler is /4 T0IE = 1; // Enable Timer Interrupts GIE = 1; // Enable Interrupts // Put hardware interface initialization code here enableBeeper(); // Enable the Beeper Output while (1 == 1) { // Loop forever // Put robot biologic code here BeeperFlag = 1; // Start Beeping Dlay(1000); // For 1 Second BeeperFlag = 0; // Stop Beepin Dlay(1000); // For 1 Second } // endwhile } // End of Mainline
The simple two-wire LCD interface is probably more appropriately used for debugging applications on the bench instead of running them on the robot (although there are deivices, like luminescent "Vacuum Florescent Displays" ("VFDs") which could display a bar graph that is visible from quite far away). The advantage of the 2 wire interface is the small amount of micrcontroller resources which are required by the application.
My first attempt at the two wire LCD interface, \code\lcd\lcd.c, was successful, but was not well designed as a robot's elelogic interface.
My second pass at coming up with a two wire LCD interface was more successful and is used in many of the sensor example applications. The source code for "\code\lcd\lcd2.c" is:
#include// 02.04.17 - LCD2: Handle LCD Functions as part of the robot // Paradigm // 02.01.27 - LCD: Display "Hello World" on an LCD // // This is a two wire interface using a 74LS174 as a shift // register and the code called from the mainline (and timed // from TMR0 interrupt) // // // Hardware Notes: // PIC16F84 Running at 4 MHz // RB1 - CLock Signal to the LCD // RB2 - Data Signal to the LCD // // Global Variables int RTC = 0; // Real Time Clock Counter volatile char LCDDlay = 20; // Initialization Delay Value volatile char LCDState = 1; // Current LCD State static volatile bit Clock @ (unsigned)&PORTB*8+1; static volatile bit ClockTRIS @ (unsigned)&TRISB*8+1; static volatile bit Data @ (unsigned)&PORTB*8+2; static volatile bit DataTRIS @ (unsigned)&TRISB*8+2; char * MessageOut; // Message to be Sent Out volatile char MessageOuti = 0; // Index to current Message Byte char Message[13] = "Hello World!"; // Message to Display char Message2[11] = "\376\3002nd Line"; // Configuration Fuses #if defined (_16F84) #warning PIC16F84 selected __CONFIG(0x03FF1); // PIC16F84 Configuration Fuses: // - XT Oscillator // - 70 msecs Power Up Timer On // - Watchdog Timer Off // - Code Protection Off #elif defined(_16F627) #warning PIC16F627 with internal oscillator selected __CONFIG(0x03F70); // PIC116F627 Configuration Fuses: // - Internal Oscillator // - RA6/RA7 Digital I/O // - External Reset // - 70 msecs Power Up Timer On // - Watchdog Timer Off // - Code Protection Off // - BODEN Enabled #else #error Unsupported PICmicro MCU selected #endif // Subroutines LCDNybble(char Nybble, char RS) { // Send Nybble to LCD unsigned int i; Data = 0; // Clear the '174 for (i = 0; i < 6; i++) { // Repeat for six bits Clock = 1; Clock = 0; // Write the "0"s into the '174 } // endfor Data = 1; // Output the "AND" Value Clock = 1; Clock = 0; Data = RS; // Output the RS Bit Value Clock = 1; Clock = 0; for (i = 0; i < 4; i++) { // Output the Nybble if ((Nybble & 0x008) != 0) Data = 1; // Output the High Order Bit else Data = 0; Clock = 1; Clock = 0; // Strobe the Clock Nybble = Nybble << 1; // Shift up Nybble for Next Byte } // endfor Data = 1; Data = 0; // Toggle the "E" Clock Bit } // End LCDNybble LCDByte(char Byte, char RS) { // Send Byteto LCD LCDNybble((Byte >> 4) & 0x00F, RS); // Send High Nybble LCDNybble(Byte & 0x00F, RS); // Send Low Nybble } // End LCDByte LCDInit() // Initialize the LCD I/O Pins { Clock = 0; Data = 0; // Low I/O Bits ClockTRIS = 0; DataTRIS = 0; } // End LCDInit LCDOut(char * const LCDString) // Output the Data String { while (LCDState); // Wait for LCD Available MessageOut = LCDString; // Load up the string LCDState = 100; // Start Sending the String } // End LCDOut // Interrupt Handler void interrupt tmr0_int(void) // TMR0 Interrupt Handler { char temp; if (T0IF) { T0IF = 0; // Reset Interrupt Flag RTC++; // Increment the Clock // Put additional interface code for 1 msec interrupt here // LCD State Machine switch(LCDState) { // Process the State Machine information case 1: // Start LCD Initialization Process if (--LCDDlay == 0) LCDState++; break; // Wait 20 msecs for LCD to reset case 2: // LCDNybble(0x003, 0); // Step 2 - Send Init Char LCDDlay = 5; LCDState++; case 3: // Wait for Command to Complete if (--LCDDlay == 0) LCDState++; break; case 4: LCDNybble(0x003, 0); // Step 3 - Send Init Char LCDState++; break; case 5: LCDNybble(0x003, 0); // Step 4 - Send Init Char LCDState++; break; case 6: LCDNybble(0x002, 0); // Step 5 - Set Operating LCDState++; // Interface Size (4 Bits) break; case 7: LCDByte(0x028, 0); // Step 6 - Set Operating LCDState++; break; case 8: LCDByte(0x008, 0); // Step 7 - Display Off LCDState++; break; case 9: LCDByte(0x001, 0); // Step 8 - Clear Display LCDState++; LCDDlay = 5; // Wait 5 msecs for Clear to Complete break; case 10: // Wait for Command to Complete if (--LCDDlay == 0) LCDState++; break; case 11: LCDByte(0x006, 0); // Step 9 - Shift LCDState++; break; case 12: LCDByte(0x00E, 0); // Step 10 - Display On LCDState = 0; // Ready to Run break; case 100: // Output a Character in "MessageOut" switch (temp = MessageOut[MessageOuti++]) { case '\0': // At the End of the Message LCDState = 0; MessageOuti = 0; // Reset Message Index break; case '\f': // Clear Display LCDByte(0x001, 0); LCDState++; LCDDlay = 5; break; case 254: // Command Byte follows if ((temp = MessageOut[MessageOuti++]) == 0) LCDState = 0; else { if (temp < 4) { LCDState++; LCDDlay = 5; } // endif LCDByte(temp, 0); } // endif break; default: // All other characters LCDByte(temp, 1); } // endswitch break; case 101: // Message Delay if (--LCDDlay == 0) LCDState--; break; } // endswitch } // endif // Put Different Interrupt Handlers here } // End Interrupt Handler // Mainline void main(void) // Template Mainline { OPTION = 0x0D1; // Assign Prescaler to TMR0 // Prescaler is /4 TMR0 = 0; // Reset the Timer for Start T0IE = 1; // Enable Timer Interrupts GIE = 1; // Enable Interrupts LCDInit(); // Initialize the LCD Port // Put in Interface initialization code here LCDOut(Message); // Pass String to Output LCDOut(Message2); // 2nd Line Message while (1 == 1) { // Loop forever // Put in Robot high level operation code here } // endwhile } // End of Mainline